package com.lottemall.item.service;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.lottemall.common.dto.CartDTO;
import com.lottemall.common.enums.ExceptionEnume;
import com.lottemall.common.exception.LtException;
import com.lottemall.common.vo.PageResult;
import com.lottemall.item.mapper.SkuMapper;
import com.lottemall.item.mapper.SpuDetailMapper;
import com.lottemall.item.mapper.SpuMapper;
import com.lottemall.item.mapper.StockMapper;
import com.lottemall.item.pojo.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 商品业务(吧Spu和Sku综合了)
 */
@Service
public class GoodsService {
    /**
     * 抽象商品mapper
     */
    @Autowired
    private SpuMapper spuMapper;

    /**
     * 抽象商品详情mapper
     */
    @Autowired
    private SpuDetailMapper spuDetailMapper;

    /**
     * 具体商品Mapper
     */
    @Autowired
    private SkuMapper skuMapper;

    /**
     * 库存mapper
     */
    @Autowired
    private StockMapper stockMapper;
    /**
     * * 商品业务实现类
     */
    @Autowired
    private CategoryService categoryService;

    /**
     * 品牌业务实现类
     */
    @Autowired
    private BrandService brandService;

    /**
     * mq消息
     */
    @Autowired
    private AmqpTemplate amqpTemplate;


    public PageResult<Spu> querySpuByPsge(Integer page, Integer rows, Boolean saleable, String key) {
        //分页
        PageHelper.startPage(page, rows);

        //过滤
        Example example = new Example(Spu.class);
        Example.Criteria criteria = example.createCriteria();
        //搜索字段过滤
        if (StringUtils.isNotBlank(key)) {
            criteria.andLike("title", "%" + key + "%");
        }
        //上下架过滤
        if (saleable != null) {
            criteria.andEqualTo("saleable", saleable);
        }
        //默认抽象商品的更新时间排序
        example.setOrderByClause("last_update_time DESC");

        //查询
        List<Spu> spuList = spuMapper.selectByExample(example);

        //判断
        if (CollectionUtils.isEmpty(spuList)) {
            throw new LtException(ExceptionEnume.GOODS_NOT_FOUND);
        }

        //解析分类进行匹配cid的名称 和brandid品牌的名称，前台需要
        loadCategoryAndBrandName(spuList);

        //解析分页结果
        PageInfo<Spu> spuPageInfo = new PageInfo<>(spuList);
        return new PageResult<>(spuPageInfo.getTotal(), spuList);
    }

    /**
     * //解析分类进行匹配cid的名称 和brandid品牌的名称，前台需要
     *
     * @param spuList 引用他的方法传过来，这里时引用内心，可以直接更改值的
     */
    private void loadCategoryAndBrandName(List<Spu> spuList) {
        for (Spu spu : spuList) {
            //处理分类名称
            //但是这里得到是实体集合，我们只想要对应的分类名称，所以不这样
//            List<Category> categoryList = categoryService.queryByIds(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()));
            List<String> namesList = categoryService.queryByIds(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3()))
                    .stream().map(Category::getName).collect(Collectors.toList());
            //然后把集合以/拼接成字符串,设置给spu中前台需要的字段
            spu.setCname(StringUtils.join(namesList, "/"));
            //处理品牌名称
            spu.setBname(brandService.queryById(spu.getBrandId()).getName());
        }
    }

    /**
     * 新增商品
     *
     * @param spu
     */
    @Transactional
    public void saveGoods(Spu spu) {
        //新增Spu
        spu.setId(null);
        spu.setCreateTime(new Date());
        spu.setLastUpdateTime(spu.getCreateTime());
        spu.setSaleable(true);//上架
        spu.setValid(false);
        int count = spuMapper.insert(spu);
        if (count != 1) {
            throw new LtException(ExceptionEnume.GOODS_SAVE_ERROR);
        }
        //新增Spu_detail
        SpuDetail spuDetail = spu.getSpuDetail();
        spuDetail.setSpuId(spu.getId());//上面如果抽象商品新增成功，那么会有id的，然后这个抽象商品详情的id是来自于上面（这里只需要设置个这个，其他在spu中已经有了）
        //直接插入表
        count = spuDetailMapper.insert(spuDetail);
        if (count != 1) {
            throw new LtException(ExceptionEnume.GOODS_SAVE_ERROR);
        }
        //新增Spu_detail
        saveSkuAndStock(spu);

        //发送mq消息(key  和 抽象商品id)
        amqpTemplate.convertAndSend("item.insert", spu.getId());
    }


    /**
     * 新增Spu_detail
     *
     * @param spu
     */
    public void saveSkuAndStock(Spu spu) {
        int count = 0;
        //新增sku 和库存
        List<Sku> skuList = spu.getSkus();
        List<Stock> stockList = new ArrayList<>();
        for (Sku sku : skuList) {
            sku.setCreateTime(new Date());
            sku.setLastUpdateTime(sku.getCreateTime());
            sku.setSpuId(spu.getId());
            count = skuMapper.insert(sku);
            if (count != 1) {
                throw new LtException(ExceptionEnume.GOODS_SAVE_ERROR);
            }
            //存入库存到集合
            Stock stock = new Stock();
            stock.setSkuId(sku.getId());
            stock.setStock(sku.getStock());
            stockList.add(stock);
        }

        //批量新增库存 (为什么商品新增sku不用批量，因为用了批量添加，sku的id就拿不到了，不过也可sku批量添加完，再查一遍）
        count = stockMapper.insertList(stockList);
        if (count == 0) {
            throw new LtException(ExceptionEnume.GOODS_SAVE_ERROR);
        }
    }

    /**
     * 根据商品SpuId查询商品SpuDetail详情
     *
     * @param spuId
     * @return
     */
    public SpuDetail querySpuDetailById(Long spuId) {
        SpuDetail spuDetail = spuDetailMapper.selectByPrimaryKey(spuId);
        if (spuDetail == null) {
            throw new LtException(ExceptionEnume.GOODS_DETAIL_NOT_FOUND);
        }
        return spuDetail;
    }

    /**
     * 根据spu查询下面所有的sku
     *
     * @param spuId
     * @return
     */
    public List<Sku> querySkuBySpuId(Long spuId) {
        Sku sku = new Sku();
        sku.setSpuId(spuId);
        //具体商品集合
        List<Sku> skuList = skuMapper.select(sku);
        if (CollectionUtils.isEmpty(skuList)) {
            throw new LtException(ExceptionEnume.GOODS_SKU_NOT_FOUND);
        }
        /*//查询库存  (但是这串代码不够优雅)
        for (Sku s : skuList) {
            Stock stock = stockMapper.selectByPrimaryKey(s.getId());
            if (stock==null){
                throw new LtException(ExceptionEnume.GOODS_STOCK_NOT_FOUND);
            }
            s.setStock(stock.getStock());
        }*/
        List<Long> ids = skuList.stream().map(Sku::getId).collect(Collectors.toList());//将集合转成生流，每次迭代里面的id，将其取出放到list集合中，取代显示for循环
        //查询库存
        loadStockInSku(ids, skuList);
        return skuList;
    }

    /**
     * 修改商品
     *
     * @param spu
     */
    @Transactional
    public void updateGoods(Spu spu) {
        if (spu.getId() == null) {
            throw new LtException(ExceptionEnume.GOODS_ID_CONNOT_BE_NULL);
        }
        Sku sku = new Sku();
        sku.setSpuId(spu.getId());
        //查询sku
        List<Sku> skuList = skuMapper.select(sku);
        if (!CollectionUtils.isEmpty(skuList)) {
            //批量删除sku  //这里因为多个sku可能属于一个spu,所以这里可以直接删除多个sku的
            skuMapper.delete(sku);
            //批量删除stock
            List<Long> ids = skuList.stream().map(Sku::getId).collect(Collectors.toList());
            stockMapper.deleteByIdList(ids);
        }
        //修改spu
        spu.setCreateTime(null);
        spu.setLastUpdateTime(new Date());
        spu.setSaleable(null);
        spu.setValid(null);
        int count = spuMapper.updateByPrimaryKeySelective(spu);
        if (count != 1) {
            throw new LtException(ExceptionEnume.GOODS_UPDATE_ERROR);
        }
        //修改detail
        count = spuDetailMapper.updateByPrimaryKeySelective(spu.getSpuDetail());
        if (count != 1) {
            throw new LtException(ExceptionEnume.GOODS_UPDATE_ERROR);
        }

        //新增sku和stock
        //为什么这里直接新增而不是修改呢
        //spu数据可以修改，但Sku数据无法修改，因为有可能之前存的Sku现在已近不在了，或者以前的sku属性都不存在了。比如以前内存4G，现在没了。
        //所以这里直接新增
        //因为sku是商品实体，spu是商品抽象，抽象下面有多个实体（是抽象商品多态的表现：一个手机有多个颜色，不同颜色价格不同）
        //比如以前的黑色手机（是属于可选中的一项）现在断货没有了，我们是不是该在sku表中删除那个多态（具体的商品实体：例如黑色手机）
        //修改sku是不可能的,因为黑色已经断货了（我们在修改的时候删除了黑色）
        //可以这样理解：由于电商之复杂 还有这个表已经设计成了抽象的概念
        // （因为页面需要给用户提供一个抽象：比如各种不一样的款式就是一个抽象，供他们选择，但是每个实体都是不一样的价格）
        //从而有了sku表的诞生，这样的话
        // 就相当于sku是spu的具体实现类，虽然在java语言中这个实现类实现了抽象类，可以有自己的特色
        //但是在表格中，应用在商品中这是不合适的，抽象商品中红蓝两色，但是sku具体商品表是取决于抽象范围中的。黑色断货了，黑色sku就没有了
        //所以为了避免这种情况首先删除spu下面的所有sku、还有这个表结构设计或思想设计本身就不能修改sku表
        //也不是说具体到钱的商品不能修改，而是这个表的设计风格以及字段类型等因素导致的。
        // 以上是大概的抽象思想，如果用数据的传统外键思想面对电商或许是个灾难，
        // 由于类型的多态多样可能导致表特多、联系十分复杂
        this.saveSkuAndStock(spu);//还有，这里可能是为了方便

        //发送mq消息(key  和 抽象商品id)
        amqpTemplate.convertAndSend("item.update", spu.getId());

    }


    /**
     * 根据id查询spu
     *
     * @param id
     * @return
     */
    public Spu querySpuById(Long id) {
        //查询Spu
        Spu spu = spuMapper.selectByPrimaryKey(id);
        if (spu == null) {
            throw new LtException(ExceptionEnume.GOODS_NOT_FOUND);
        }
        //查询sku
        spu.setSkus(querySkuBySpuId(id));
        //查询detail
        spu.setSpuDetail(querySpuDetailById(id));
        return spu;
    }

    /**
     * 根据spu的id集合查询所有sku （暂时用于前台购物车根据浏览器存储的skuids来检查商品是否改变）
     *
     * @param ids
     * @return
     * @Get http://api.lottemall.com/api/item/sku/list/ids?ids=3924115,27359021790,27359021788
     */
    public List<Sku> querySkuByIds(List<Long> ids) {
        List<Sku> skuList = skuMapper.selectByIdList(ids);
        if (CollectionUtils.isEmpty(skuList)) {
            //商品不存在
            throw new LtException(ExceptionEnume.GOODS_NOT_FOUND);
        }
        loadStockInSku(ids, skuList);
        return skuList;
    }

    /**
     * 封装好的查询库存，因为很多的sku，我们需要把他的库存查出来
     *
     * @param ids
     * @param skuList
     */
    public void loadStockInSku(List<Long> ids, List<Sku> skuList) {
        //查询库存
        List<Stock> stockList = stockMapper.selectByIdList(ids);
        if (CollectionUtils.isEmpty(stockList)) {
            throw new LtException(ExceptionEnume.GOODS_STOCK_NOT_FOUND);
        }
        //我们把stock变成一个map,其key是：sku的id，值是库存值
        Map<Long, Long> stockMap = stockList.stream().collect(Collectors.toMap(Stock::getSkuId, Stock::getStock));
        //遍历循环商品实体sku，根据上面stockMap中根据当前循环到的sku的id得到对应的商品库存，设置到当前skuList商品集合中（草拟吗真优雅，优雅得差点看不懂）
        skuList.forEach(s -> s.setStock(stockMap.get(s.getId())));
    }

    /**
     * 减库存
     *
     * @param cartDTOS
     */
    @Transactional
    public void decreaseStock(List<CartDTO> cartDTOS) {
        for (CartDTO cartDTO : cartDTOS) {
            //减库存
            int count = stockMapper.decreaseStock(cartDTO.getSkuId(), cartDTO.getNum());
            if (count != 1) {
                throw new LtException(ExceptionEnume.STOCK_NOT_ENOUGH);
            }
        }
    }
}
